package com.pony.iphone.mq;

import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author pony
 * @description: 钉钉消息频率限制器，
 *  防止钉钉机器人因发送消息过于频繁而被限流
 *  钉钉官方限制：每个机器人每分钟最多发送20条消息
 */
@Slf4j
@Component
public class DingTalkRateLimiter {

    // 每分钟最多18条 0.3：每秒生成 0.3 个令牌 ≈ 每分钟 18 个令牌 (平均每 3.3 秒发送一条消息)
    // final修饰：整个应用生命周期只需要一个实例
    private final RateLimiter rateLimiter = RateLimiter.create(0.3);
    // 原子计数器，记录当前分钟已发送的消息数
    private final AtomicInteger messageCount = new AtomicInteger(0);
    // 上次重置计数器的时间戳
    private long lastResetTime = System.currentTimeMillis();

    /**
     * 检查是否允许发送钉钉消息
     * @return true：允许发送，false：不允许发送
     */
    public boolean allowSend() {
        // 1、检查是否需要重置计数器
        resetCounterIfNeeded();
        
        // 2、检查分钟限制
        if (messageCount.get() >= 18) {
            log.warn("已达到钉钉消息频率限制，当前计数: {}", messageCount.get());
            return false;
        }
        
        // 3、使用令牌桶控制瞬时频率，如果令牌桶中有令牌就会允许发送并计数，平均每 3.3 秒发送一条消息
        if (rateLimiter.tryAcquire()) {
            // 原子计数器 -1，已经发送出去一条信息了
            messageCount.incrementAndGet();
            return true;
        }
        
        return false;
    }

    /**
     * 检查是否需要重置计数器
     */
    private void resetCounterIfNeeded() {
        // 当前时间戳
        long currentTime = System.currentTimeMillis();
        // 当前时间戳-上次重置时的时间戳 > 一分钟，也就是说进入下一个分钟了
        if (currentTime - lastResetTime > 60 * 1000) {
            // 将计数器清零
            messageCount.set(0);
            // 重置上次重置时间
            lastResetTime = currentTime;
            log.info("钉钉消息计数器已重置");
        }
    }
    
    public int getCurrentCount() {
        resetCounterIfNeeded();
        return messageCount.get();
    }
}